"use strict";

const appRoot = require('app-root-path');
const PGMixin = require(appRoot + "/mixins/postgres.mixin");
const CacheCleanerMixin = require(appRoot + "/mixins/cache.cleaner.mixin");
const scheduleModel = require(appRoot + "/pgmodels/scheduleModel");
const moment = require('moment');
const {QueryTypes,Op,Transaction} = require('sequelize');
const osSystem = require("os");

/**
 * 抢取任务，生成任务执行记录
 */
module.exports = {
	name: "schedule",
	version:1,
	mixins: [PGMixin('schedule'),
		CacheCleanerMixin([
			"cache.clean.schedule",
		])],
	model:scheduleModel,


	settings:{
		auth:"ignore",
		excludeDBService:['list','create','get','update','remove'],//屏蔽所有框架自动生成的接口
		timeout:5 * 60 * 1000, //任务超时设置 单位毫秒
	},

	/**
	 * Actions
	 */
	actions: {

		/**
		 * 抢任务，抢到任务
		 */
		robTask: {
			params:{
				namespace:{  type:"string", max:200, optional:false },
				projectName:{  type:"string", max:200, optional:false },
				taskName:{  type:"string", max:200, optional:false },
				hostName:{  type:"string", max:200, optional:false },
				nodeId:{  type:"string", max:200, optional:false },
				pid:{ type:"number", optional:false, convert:true, integer:true, min:1 },
				ppid:{ type:"number", optional:false, convert:true, integer:true, min:1 }
			},
			async handler(ctx) {
				let params = this.sanitizeParams(ctx,ctx.params);
				let cronTask = undefined;
				let transaction = await this.adapter.db.transaction({
					autocommit: false,
					isolationLevel: Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED,
				});
				try {
					let sql = `  SELECT id,
										namespace,
										project_name AS "projectName",
										task_name AS "taskName"
								FROM sys_task
								WHERE namespace = ?
									AND project_name = ?
									AND task_name = ?
									AND is_enable = ?
								LIMIT 1
								FOR UPDATE `;
					const [ task, rowsMetadata ] = await this.adapter.db[ 'query' ](sql, {
						replacements:[params.namespace,params.projectName,params.taskName,true],
						type:QueryTypes.SELECT,
						transaction:transaction
					});

					if(task){

						/**
						 * 如果有 5 (this.settings.taskTimeout) 分钟内未完成的任务，
						 * 或者 1 分钟内已完成的任务，就认为此任务已存在，放弃此次执行任务
						 */
						const existsSchedule = await this.adapter.findOne({
							where:{
								namespace: task.namespace,
								projectName: task.projectName,
								taskName: task.taskName,
								[ Op.or ]:[{
									isFinished:false,
								},{
									isFinished:true,
									updatedTime:{
										[ Op.gte ]:moment().subtract(30*1000, "ms").format("YYYY-MM-DD HH:mm:ss.SSS"),
									},
								}]
							},
							order: [
								['id', 'DESC'],
							],
						});

						if(!existsSchedule){
							cronTask = await this.adapter.insert({
								taskId: task.id,
								namespace: task.namespace,
								projectName: task.projectName,
								taskName: task.taskName,
								hostName: params.hostName,
								nodeId: params.nodeId,
								pid: params.pid,
								ppid: params.ppid,
								createdTime:moment().format('YYYY-MM-DD HH:mm:ss.SSS'),
								isFinished:false
							});
							sql = ` UPDATE sys_task  SET updated_time = NOW(),execute_count = execute_count+1 WHERE id = ? ;`;
							await this.adapter.db[ 'query' ](sql, {
								replacements:[task.id],
								type:QueryTypes.UPDATE,
								transaction:transaction
							});
						}
					}
					await transaction.commit();
				}catch (ex) {
					await transaction.rollback();
					this.logger.error(ex);
				}
				// finally {
				// 	this.logger.error("game is over.");
				// }
				return cronTask;
			}
		},

		/**
		 * 将任务的 更新时间修改为当前时间，保持任务为活跃状态
		 */
		keepTaskLive: {
			params:{
				id:{ type:"number", optional:false, convert:true, integer:true, min:1 },
			},
			async handler(ctx) {
				return await this.adapter.model.update({
					updatedTime:moment().format('YYYY-MM-DD HH:mm:ss'),
				}, {
					where:{
						id:ctx.params.id,
						isFinished:false,
					},
				});
			}
		},

		/**
		 * 更新任务状态为完成
		 */
		updateTaskFinished: {
			params:{
				id:{ type:"number", optional:false, convert:true, integer:true, min:1 },
				isSuccess:{ type:"boolean", optional:false },
			},
			async handler(ctx) {
				return await this.adapter.model.update({
					isSuccess:ctx.params.isSuccess,
					isFinished:true,
					updatedTime:moment().format('YYYY-MM-DD HH:mm:ss'),
				}, {
					where:{
						id:ctx.params.id
					},
				});
			}
		},

		/**
		 * 更新超时任务，认为其是失败任务，超时设置 settings.taskTimeout 单位毫秒
		 */
		updateTaskTimeoutFailure: {
			params:{
				timeout:{ type:"number", optional:false, convert:true, integer:true, description:"超时设置，单位毫秒" },
			},
			async handler(ctx) {
				let params = this.sanitizeParams(ctx,ctx.params);
				this.logger.info(moment().subtract(this.settings.taskTimeout, 'ms').format('YYYY-MM-DD HH:mm:ss'));
				return await this.adapter.model.update({
					isSuccess:false,
					isFinished:true,
					updatedTime:moment().format('YYYY-MM-DD HH:mm:ss'),
				}, {
					where:{
						createdTime:{
							[ Op.lte ]:moment().subtract(params.timeout, 'ms').format('YYYY-MM-DD HH:mm:ss'),
						},
						isFinished:false
					},
				});
			}
		},

	},

	/**
	 * Events
	 */
	events: {

	},

	/**
	 * Methods
	 */
	methods: {

	},

	/**
	 * Service created lifecycle event handler
	 */
	created() {

	},

	/**
	 * Service started lifecycle event handler
	 */
	async started() {

	},

	/**
	 * Service stopped lifecycle event handler
	 */
	async stopped() {

	}
};
